﻿using System;
using System.Net;
using System.Linq;
using Tulde.Core;
using Tulde.MP3tunes.Model;
using System.Collections.Generic;
using Tulde.MP3tunes.Model;

namespace Tulde.MP3tunes
{
    /// <summary>
    /// Using the MediaAPI provided by Mp3Tunes.com, retrieves data elements from a user's locker.  All data results are
    /// currently returned in JSON format.
    /// 
    /// To use this API, you must have a valid locker account with a partner token and username/password.
    /// </summary>
    public static class Mp3TunesMusicService
    {
        public static event EventHandler<DataServiceEventArgs> Error;
        private static void OnError(DataServiceEventArgs args)
        {
            if (Error != null)
            {
                Error(null, args);
            }
        }
        private static void ProcessError(object sender, DataServiceEventArgs args)
        {
            OnError(args);
        }
        #region Model Enumeration Members (Fills a ModelCollection with a list of Model Instances)
        /// <summary>
        /// Enumerates all artists existing in a locker.
        /// <example>
        /// <![CDATA[
        /// No need to call this.Dispatch because we are already executing on the UI Thread.
        /// instance.EnumerateArtists( result=> {
        ///      this.myDataBoundControl.DataContext = result.ArtistList;
        /// } );
        /// 
        /// ]]>
        /// 
        /// </example>
        /// </summary>
        /// <param name="callback">Callback databinding lambda</param>
        public static void EnumerateArtists(Action<ModelCollection> callback, Action<Exception> error=null)
        {
            Download<ModelCollection>(ServiceTypes.artist, callback, error);

        }
        /// <summary>
        /// Enumerates all albums existing in a locker.
        /// <example>
        /// <![CDATA[
        /// No need to call this.Dispatch because we are already executing on the UI Thread.
        /// instance.EnumerateAlbums( result=> {
        ///      this.myDataBoundControl.DataContext = result.albumList;
        /// } );
        /// 
        /// ]]>
        /// 
        /// </example>
        /// </summary>
        /// <param name="callback">Callback databinding lambda</param>
        public static void EnumerateAlbums(Action<ModelCollection> callback, Action<Exception> error = null)
        {
           
            Download<ModelCollection>(ServiceTypes.album, callback, error);

        }
        /// <summary>
        /// Enumerates all tracks existing in a locker.
        /// <example>
        /// <![CDATA[
        /// No need to call this.Dispatch because we are already executing on the UI Thread.
        /// instance.EnumerateTracks( result=> {
        ///      this.myDataBoundControl.DataContext = result.TrackList;
        /// } );
        /// 
        /// ]]>
        /// 
        /// </example>
        /// </summary>
        /// <param name="callback">Callback databinding lambda</param>
        public static void EnumerateTracks(Action<ModelCollection> callback, Action<Exception> error = null)
        {

            Download<ModelCollection>(ServiceTypes.track, callback, error);
        }
        /// <summary>
        /// Enumerates all videos existing in a locker.
        /// <example>
        /// <![CDATA[
        /// No need to call this.Dispatch because we are already executing on the UI Thread.
        /// instance.EnumerateTracks( result=> {
        ///      this.myDataBoundControl.DataContext = result.TrackList;
        /// } );
        /// 
        /// ]]>
        /// 
        /// </example>
        /// </summary>
        /// <param name="callback">Callback databinding lambda</param>
        public static void EnumerateVideos(Action<ModelCollection> callback, Action<Exception> error = null)
        {

            Download<ModelCollection>(ServiceTypes.video, callback, error);
        }
        /// <summary>
        /// Enumerates all playlists existing in a locker.
        /// <example>
        /// <![CDATA[
        /// No need to call this.Dispatch because we are already executing on the UI Thread.
        /// instance.EnumeratePlaylists( result=> {
        ///      this.myDataBoundControl.DataContext = result.PlaylistList;
        /// } );
        /// 
        /// ]]>
        /// 
        /// </example>
        /// </summary>
        /// <param name="callback">Callback databinding lambda</param>
        public static void EnumeratePlaylists(Action<ModelCollection> callback, Action<Exception> error = null)
        {

            Download<ModelCollection>(ServiceTypes.playlist, callback, error);
        }
        #endregion

        /// <summary>
        /// Returns a list of all albums for a given artist.
        /// </summary>
        /// <param name="artist"></param>
        /// <param name="callback"></param>
        public static void GetArtistAlbums(Artist artist, Action<ModelCollection> callback, Action<Exception> error = null)
        {
           
            LockerDataService<ModelCollection> dataService = new LockerDataService<ModelCollection>();
            Uri url = dataService.CreateServiceEndpoint(LockerDataOperations.lockerData, new DataServiceParameters
            {
                ServiceType = ServiceTypes.album,
                ArtistServiceKey = artist.ArtistId
            });
            dataService.ExecuteAsync<ModelCollection>(url, callback);

            
        }
        /// <summary>
        /// Returns all tracks for a given album.
        /// </summary>
        /// <param name="album">The album  for which to find tracks.</param>
        /// <param name="callback"></param>
        public static void GetAlbumTracks(string albumId,string artistId, Action<ModelCollection> callback)
        {
            LockerDataService<ModelCollection> dataService = new LockerDataService<ModelCollection>();

            Uri url = dataService.CreateServiceEndpoint(LockerDataOperations.lockerData, new DataServiceParameters
              {
                  ServiceType = ServiceTypes.track,
                  AlbumServiceKey = albumId,
                  ArtistServiceKey = artistId,
              });
            dataService.ExecuteAsync<ModelCollection>(url, callback);
        }


        /// <summary>
        /// Authenticate a user 
        /// </summary>
        /// <param name="credentials"></param>
        /// <param name="callback"></param>
        public static void Authenticate(ISecurityCredentials credentials, Action<AuthenticationToken> callback)
        {
            SecurityProvider.SetCredentials(credentials);
            AuthenticationService s = new AuthenticationService();
            Uri url = s.CreateServiceEndpoint(AuthenticationOperations.login, new AuthenticationServiceParameters
            {
                UserName = SecurityProvider.Credentials.UserName,
                Password = SecurityProvider.Credentials.Password
            });

            s.ExecuteAsync<AuthenticationToken>(url, callback);

        }
        /// <summary>
        /// Playlist change especially shuffle, so we will not cache any of these items.
        /// </summary>
        /// <param name="playlist"></param>
        /// <param name="callback"></param>
        public static void GetPlaylist(string playlistId, Action<ModelCollection> callback)
        {
            System.Net.WebClient client = new WebClient();
            LockerDataService<ModelCollection> dataService = new LockerDataService<ModelCollection>();
            Uri url = dataService.CreateServiceEndpoint(LockerDataOperations.lockerData, new DataServiceParameters
            {
                ServiceType = ServiceTypes.track,
                PlaylistServiceKey = playlistId
            });
            dataService.ExecuteAsync<ModelCollection>(url, callback);
        }

        public static void GetLastUpdate(Action<Checksum> checksum)
        {
            System.Net.WebClient client = new WebClient();
            LockerDataService<Checksum> dataService = new LockerDataService<Checksum>();
            Uri url = dataService.CreateServiceEndpoint(LockerDataOperations.lastUpdate, new DataServiceParameters
            {
                ServiceType = ServiceTypes.locker

            });
            dataService.ExecuteAsync<Checksum>(url, checksum);
        }
        /// <summary>
        /// Download all artists, albums, tracks, videos and playlists.
        /// </summary>
        /// <param name="complete"></param>
        public static void DownloadLockerData(Action<ModelCollection> complete, Action<Exception> error)
        {
            LogProviderFactory.Instance.Debug(0, global::Tulde.MP3tunes.Strings.DownloadMessage1);
            //ongoing operations.  can't continue.
            ModelCollection model = new Model.ModelCollection();
            if (_operationStack != null && !IsEmpty())
            {
                return;
            }
            _operationStack = new Stack<int>();

            AddOperation(0);
            AddOperation(2);
            AddOperation(3);
            AddOperation(4);
            // AddOperation(5);

            EnumerateArtists(callback =>
                {
                   // model.Artists.AddRange(callback.Artists);
                    model.Artists = callback.Artists;
                    Remove();
                    if (IsEmpty())
                    {
                        if (complete != null)
                            complete(model);
                    }
                }, error);
      

            EnumerateAlbums( callback =>
            {
                //model.Albums.AddRange(callback.Albums);
                model.Albums = callback.Albums;
                Remove();
                if (IsEmpty())
                {
                    if (complete != null)
                        complete(model);
                }
            }, error);

            EnumeratePlaylists( callback =>
            {
               // model.Playlists.AddRange(callback.Playlists);
                model.Playlists = callback.Playlists;
                Remove();
                if (IsEmpty())
                {
                    if (complete != null)
                        complete(model);
                }
            }, error);

            //Download<TrackCollection>(ServiceTypes.track,callback =>
            //    {
            //        _tracks = callback.Tracks;
            //        Remove();
            //        if (IsEmpty())
            //        {
            //            complete(cache);
            //        }
            //    });

            EnumerateVideos(callback =>
                {
                    //model.Tracks.AddRange(callback.Tracks);
                    model.Tracks = callback.Tracks;
                    Remove();
                    if (IsEmpty())
                    {
                        if (complete != null)
                            complete(model);
                    }
                }, error);
        }
        private static void Download<T>(ServiceTypes type, Action<T> callback, Action<Exception> error)
        {
            LockerDataService<T> dataService = new LockerDataService<T>();
            dataService.ExecuteAsync<T>(LockerDataOperations.lockerData,
                new DataServiceParameters
                {

                    ServiceType = type,
                    MiniData = true
                }
               , callback, error);
        }



        private static Stack<int> _operationStack = null;
        private static object _lockSynch = new object();

        private static void AddOperation(int id)
        {
            lock (_lockSynch)
            {
                _operationStack.Push(id);
            }
        }
        private static bool IsEmpty()
        {
            lock (_lockSynch)
            {
                return _operationStack.Count > 0 ? false : true;
            }
        }
        private static void Remove()
        {
            lock (_lockSynch)
            {
                _operationStack.Pop();
            }
        }
     

    }
}
